﻿using JX.Infrastructure.Common;
using JX.Infrastructure.NLog;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;

namespace JX.Infrastructure.Framework.Jwt
{
	/// <summary>
	/// jwt帮助类
	/// </summary>
	public static class JwtHelper
	{
		/// <summary>
		/// 得到JwtToken
		/// </summary>
		/// <param name="jwtUserInfo">身份结果类，里面包含有用户的身份信息</param>
		/// <param name="jwtConfig">JWT配置类</param>
		/// <returns></returns>
		public static JwtResult GetJwtResult(JwtUserInfo jwtUserInfo, JwtConfig jwtConfig)
		{
			JwtResult jwtResult = new JwtResult();
			if (jwtUserInfo == null)
			{
				jwtResult.Status = false;
				jwtResult.Msg = "jwtUserInfo参数为NULL或不包含身份信息";
				return jwtResult;
			}

			//把身份信息加入缓存
			var RefreshToken = Guid.NewGuid().ToString("N");
			var cacheKey = $"RefreshToken:{RefreshToken}";
			var cacheValue = JsonConvert.SerializeObject(jwtUserInfo);
			CacheHelper.CacheServiceProvider.AddOrUpdate(cacheKey, cacheValue, TimeSpan.FromMinutes(jwtConfig.RefLifetime));

			var now = DateTime.UtcNow;
			var AccessTokenExpired = now.Add(TimeSpan.FromMinutes(jwtConfig.Lifetime));
			var RefreshTokenExpired = now.Add(TimeSpan.FromMinutes(jwtConfig.RefLifetime));
			var claims = new List<Claim>()
			{
				new Claim(ClaimTypes.Sid,jwtUserInfo.Sid.ToString())
				,new Claim(ClaimTypes.Name,jwtUserInfo.Name)
				,new Claim(ClaimTypes.Role,jwtUserInfo.Role)
			};
			//jwt签发者
			claims.Append(new Claim(JwtRegisteredClaimNames.Sub, jwtUserInfo.Name));
			//jwt的唯一标识
			claims.Append(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));
			//jwt颁发的时间，采用标准unix时间，用于验证过期
			claims.Append(new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64));
			//生成SymmetricSecurityKey密钥
			var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtConfig.Secret));
			var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
			var jwt = new JwtSecurityToken(
					issuer: jwtConfig.Issuer,
					audience: jwtConfig.Audience,
					claims: claims,
					notBefore: now,
					expires: AccessTokenExpired,
					signingCredentials: signingCredentials
				);
			var AccessToken = new JwtSecurityTokenHandler().WriteToken(jwt);
			//把JwtToken加入黑名单缓存
			var cacheValueJwt = new { IsEnable = true, CreateTime = now.ToUniversalTime() };
			CacheHelper.CacheServiceProvider.AddOrUpdate($"JwtToken:{AccessToken}", JsonConvert.SerializeObject(cacheValueJwt), TimeSpan.FromMinutes(jwtConfig.Lifetime));

			jwtResult.Status = true;
			jwtResult.Msg = "生成TOKEN成功";
			jwtResult.AccessToken = AccessToken;
			jwtResult.RefreshToken = RefreshToken;
			jwtResult.AccessTokenExpired = Utility.ConvertTimeToMillis(AccessTokenExpired);
			jwtResult.RefreshTokenExpired = Utility.ConvertTimeToMillis(RefreshTokenExpired);
			return jwtResult;
		}

		/// <summary>
		/// 验证TOKEN
		/// </summary>
		/// <param name="token"></param>
		/// <param name="jwtConfig"></param>
		/// <param name="Clims"></param>
		/// <returns></returns>
		public static bool ValidateToken(string token, JwtConfig jwtConfig, out List<Claim> Clims)
		{
			Clims = new List<Claim>();
			if (string.IsNullOrWhiteSpace(token))
			{
				return false;
			}

			//验证TOKEN是否在黑名单中
			var cacheUser = CacheHelper.CacheServiceProvider.Get($"JwtToken:{token}");
			if (cacheUser != null)
			{
				var dummyObject = new { IsEnable = true, CreateTime = DateTime.UtcNow };
				var cacheValueJwt = JsonConvert.DeserializeAnonymousType(cacheUser.ToString(), dummyObject);
				if (!cacheValueJwt.IsEnable)
				{
					return false;
				}
			}

			var handler = new JwtSecurityTokenHandler();
			try
			{
				var jwt = handler.ReadJwtToken(token);
				if (jwt == null)
				{
					return false;
				}

				//生成SymmetricSecurityKey密钥
				var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtConfig.Secret));
				var validationParameters = new TokenValidationParameters
				{
					RequireExpirationTime = true,
					IssuerSigningKey = signingKey,
					ClockSkew = TimeSpan.Zero,
					ValidateIssuer = true,//是否验证Issuer
					ValidateAudience = true,//是否验证Audience
					ValidateLifetime = true,//是否验证失效时间
					ValidateIssuerSigningKey = true,//是否验证SecurityKey
					ValidAudience = jwtConfig.Audience,
					ValidIssuer = jwtConfig.Issuer
				};
				SecurityToken securityToken;
				ClaimsPrincipal principal = handler.ValidateToken(token, validationParameters, out securityToken);
				foreach (var item in principal.Claims)
				{
					Clims.Add(new Claim(item.Type, item.Value));
				}
				return true;
			}
			catch (Exception ex)
			{
				NLogHelper.Error("jwt验证失败！", ex);
				return false;
			}
		}

		/// <summary>
		/// 刷新TOKEN
		/// </summary>
		/// <param name="token">RefreshToken</param>
		/// <param name="userID">用户ID</param>
		/// <param name="jwtConfig"></param>
		/// <returns></returns>
		public static JwtResult RefreshToken(string token, int userID, JwtConfig jwtConfig)
		{
			JwtResult jwtResult = new JwtResult();
			if (string.IsNullOrWhiteSpace(token))
			{
				jwtResult.Status = false;
				jwtResult.Msg = "RefreshToken不能为空";
				return jwtResult;
			}
			var cacheUser = CacheHelper.CacheServiceProvider.Get($"RefreshToken:{token}");
			JwtUserInfo jwtUserInfo = JsonConvert.DeserializeObject<JwtUserInfo>(cacheUser.ToString());
			if (jwtUserInfo == null)
			{
				jwtResult.Status = false;
				jwtResult.Msg = "RefreshToken不存在或已过期";
				return jwtResult;
			}

			if (jwtUserInfo.Sid != userID)
			{
				jwtResult.Status = false;
				jwtResult.Msg = "用户不匹配";
				return jwtResult;
			}
			jwtResult = GetJwtResult(jwtUserInfo, jwtConfig);
			if (jwtResult.Status)
			{
				CacheHelper.CacheServiceProvider.Remove($"RefreshToken:{token}");
			}
			return jwtResult;
		}
	}

	/// <summary>
	/// jwt中用到的身份信息
	/// </summary>
	public class JwtUserInfo
	{
		/// <summary>
		/// 会员ID
		/// </summary>
		public int Sid { get; set; } = 0;

		/// <summary>
		/// 会员名
		/// </summary>
		[DisplayFormat(ConvertEmptyStringToNull = false)]
		public string Name { get; set; } = string.Empty;

		/// <summary>
		/// 角色名
		/// </summary>
		[DisplayFormat(ConvertEmptyStringToNull = false)]
		public string Role { get; set; } = string.Empty;
	}
}
